/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package rna;

/**
 *
 * @author nathy
 */
public class RedeNeural {

    double taxa = 0.1;
    double eq, eqm = 0.0;
    double eqm_atual = 1.0;
    double eqm_anterior = 987654321987654.0;
    double precisao;
    double yDesejado;
    double erro;
    Camada camadas[];
    double x[];                 // Entrada de dados da rede neural;
    Arquivo arquivo;
    int numTreinamentos;
    static int numDePadroes = 200;
    double yFinal = 0.0;

    public RedeNeural(int treinamentos) {

        numTreinamentos = treinamentos;
        camadas = new Camada[3];                   // Determina o numero de camadas da rede;
        camadas[0] = new Camada("entrada", 3);
        camadas[1] = new Camada("oculta", 2);      // Determina o tipo e a quantidade de neuronios por camada;
        camadas[2] = new Camada("saida", 1);

        this.setEntradas(9);                       // Determina o numero de entradas da primeira camada;

        arquivo = new Arquivo();                   // Carrega o conjunto de dados do arquivo para a memoria;
        arquivo.leArquivo();
    }

    public void setTaxa(double taxa2) {
        taxa = taxa2;
    }

    public double getPrecisao() {
        return precisao;
    }

    public void setPrecisao(double precisao) {
        this.precisao = precisao;
    }

    public Camada[] getVetorCamadas() {
        return camadas;
    }

    public void setVetorCamadas(int numCamadas) {
        camadas = new Camada[numCamadas];
    }

    /* Baseando-se no numero de neuronios da camada anterior, determina a quantidade de entradas da camada atual,
     * onde 'numEntradas' eh a quantidade de entradas dos neuronios da primeira camada;
     */
    private void setEntradas(int numEntradas) {

        x = new double[numEntradas];

        for (int i = 0; i < camadas.length; i++) {

            if (i > 0) {
                camadas[i].setNeuronios(camadas[i - 1].getNumeroDeNeuronios());
            } else {
                camadas[i].setNeuronios(numEntradas);
            }
        }
    }

//    public double[][] geraPeso(int numNeuronios, int numEntradas) {
//        double matrizPeso[][];
//        matrizPeso = new double[numNeuronios][numEntradas];
//
//        for (int cont = 0; cont < numNeuronios; cont++) {
//            for (int cont2 = 0; cont2 < numEntradas; cont2++) {
//
//                double temp = Math.random();
//                double t = (temp -(int) temp) * 0.5;
//               // System.out.println("pesos: " + t);
//                matrizPeso[cont][cont2] = t;
//            }
//        }
//        return matrizPeso;
//    }

public void feedForward()
{
//for(int epoca = 0; epoca < numTreinamentos; epoca++) {
    while(Math.abs(eqm_atual - eqm_anterior) >Math.pow(10, -6))
    {
System.out.println("A diferenca entre os eqm eh"+Math.abs(eqm_atual - eqm_anterior) );
        eqm_anterior = eqm_atual;

        for(int padrao = 0; padrao <= numDePadroes; padrao++)
        {
                //Define saida desejada
                yDesejado = arquivo.conjuntoDeDados.get(padrao)[9];
             //   System.out.println("Saida"+yDesejado);

                for (int cont2 = 0; cont2 < camadas.length; cont2++) {
                    if (cont2 == 0) {
                        for (int cont = 0; cont < camadas[0].entradas.length; cont++) {
                            camadas[0].entradas[cont] = arquivo.conjuntoDeDados.get(padrao)[cont];
                        }
                    } else
                        camadas[cont2].entradas = camadas[(cont2 - 1)].saidas;

                    for (int cont = 0; cont < camadas[cont2].getNumeroDeNeuronios(); cont++) {

                        camadas[cont2].neuronios[cont].processar();
                        camadas[cont2].saidas[cont] = camadas[cont2].neuronios[cont].saida;
                    }
                }

                yFinal = camadas[camadas.length - 1].saidas[0];
                erro = yDesejado - yFinal;
              //  System.out.println("O erro eh"+ erro);

//                System.out.println("Saida final: " + yFinal);
//                System.out.println("Saida desejada: " + yDesejado);
//                System.out.println("erro: "+ erro+"\n");

                backPropagation();

                eq = eq + 0.5 * Math.pow(erro, 2);
            }   // Fim do processanmento de um padrao de entrada

        // ao chegar aqui, a rede ja processou a quantidade de padroes
        // especificadas na variavel numDePadroes;

        eqm = eq/numDePadroes;
        eqm_atual = eqm;
//        System.out.println("Erro quadratico medio: " +eqm);
//        System.out.println("Eqm anterior"+ eqm_anterior);
//        System.out.println("Eqnm atual"+ eqm_atual);
    }   // Fim do while

         eq = 0.0;
         eqm_atual = 1.0;
         eqm_anterior = 987654321987654.0;

    //}       // Fim do numero de epocas;
}

public void backPropagation(){

// Para cada camada...
   for(int cont1 = camadas.length-1; cont1 >= 0; cont1--) {
// Para cada neuronio da camada...
        for(int cont2 = camadas[cont1].numeroDeNeuronios-1; cont2 >= 0; cont2--) {
// Para cada entrada do neuronio...
            for(int cont3 = camadas[cont1].entradas.length-1; cont3 >= 0; cont3--) {
                double pesoAnterior = camadas[cont1].neuronios[cont2].pesos[cont3];

                double isaida = camadas[cont1].neuronios[cont2].saida;  // Mudei akiii
                double delta  = 0;
                double valor = 0;
                if(cont1 == camadas.length-1){  // Se for a camada de saida:
                        delta = getDeltaSaida(erro,isaida);
                        camadas[cont1].neuronios[cont2].delta = delta;
                 }
                else {       // Se for uma camada oculta ou de entrada:
                    delta = 0;
                    for(int cont4 = 0; cont4 < camadas[cont1+1].numeroDeNeuronios; cont4++){
                        /* delta eh o somatorio dos deltas de cada neuronio da camada
                         da frente, multiplicado pelo peso da conexao - cont2
                         se refere ao neuronio que esta tendo seu peso alterado */
                        delta = delta + camadas[cont1+1].neuronios[cont4].delta
                                * camadas[cont1+1].neuronios[cont4].pesos[cont2];
                    }
                    delta = calculeGLinha(isaida)* delta;
                    camadas[cont1].neuronios[cont2].delta = delta;
                }
                 if(cont1 == 0)
                 {   // Se for a camada d entrada a rede...
                    valor = camadas[cont1].entradas[cont3];
                 }
                 else
                 {
                    // Pega a saida do neuronio que esta conectado ao peso
                    // a ser modificado
                    valor = camadas[cont1-1].neuronios[cont3].saida;
                 }
                    camadas[cont1].neuronios[cont2].setPeso(cont3,calculaPeso(pesoAnterior, delta, valor));
            }
        }
    }
    return ;
}

    public double getDeltaSaida(double erro, double Isaida){
            return (erro) * calculeGLinha(Isaida);
    }

    public double calculeGLinha(double lSaida){

        return 0.5 * (1 / ( 1 + Math.exp(-0.5 * lSaida) )) * (1 - 1 / ( 1 + Math.exp(-0.5 * lSaida) ));
    }

    public double calculaPeso(double pesoAnterior,
            double delta, double valor) {

        return (pesoAnterior + taxa * delta * valor);
    }
}

